/**
 * author: redd
 * create_time: 2018/1/12 10:42
 * description:
 *
 */
 
function Game() {
  this.scale = 50;
  this.step = 2;
  this.moveScale = 2;
  this.score = 0;
  this.playing = true;
  
  this.offsetx = 0; //x轴方向当前偏移距离
  this.offsetz = 0;
  this.oldOffsetx = 0; //上一个对称轴距离
  this.oldOffsetz = 0;
  
  this.boxes = [];
  var isMobile = /(iPhone|iPod|Android|ios|mobile)/i.test(navigator.userAgent);
  this.downEvent = isMobile ? 'touchstart' : 'mousedown';
  this.upEvent = isMobile ? 'touchend': 'mouseup';
  
  this.jumpReady = false;

  this.size = {
    width: window.innerWidth,
    height: window.innerHeight
  }
  
  this.maskDom = document.querySelector('#J_mask');
  this.scoreDom = document.querySelector('#J_mask .score');
  this.buttonDom = document.querySelector('#J_mask button');
  this.topScoreDom = document.querySelector('#J_text');
}

Game.prototype = {
  init: function () {
    this._resetOffset();
    this._initRenderer();
    this._initScene();
    this._initCamera();
    this._initLight();
    this._addBox();
    this._addBox();
    this._addPlayer();
    this._updateCamera();
    this._render();

    var self = this;
    window.addEventListener('resize', function () {
      self._handleWindowResize();
    })
    
    window.addEventListener('contextmenu', function (e) {
      e.preventDefault();
    })
  
    window.addEventListener(this.downEvent, function (e) {
      if(!self.playing) {
        return;
      }
      self._handleMouseDown();
    })
    
    window.addEventListener(this.upEvent, function (e) {
      if(!self.playing) {
        return;
      }
      self._handleMouseup();
    })
    
  },
  _resetOffset: function () {
    this.offsetx = 0;
    this.offsetz = 0;
    this.oldOffsetx = 0;
    this.oldOffsetz = 0;
  },
  _setSize: function () {
    this.size.width = window.innerWidth;
    this.size.height = window.innerHeight;
  },
  _initRenderer: function () {
    this.renderer = new THREE.WebGLRenderer({
      canvas: document.getElementById('canvas'),
      antialias:true
    });
    this.renderer.setSize(this.size.width, this.size.height);
    this.renderer.setClearColor(0x292929);
  },
  _initScene: function () {
    this.scene = new THREE.Scene();
  },
  _initCamera: function () {
    var width = this.size.width;
    var height = this.size.height;
    var scale = this.scale;

    this.camera = new THREE.OrthographicCamera(-width/scale, width/scale, height/scale, -height/scale, 0, 100000);
    this.camera.position.set(500, 500, 500);
    this.camera.curPos = new THREE.Vector3(0, 0, 0);
    this.camera.targetPos = new THREE.Vector3();
    this.camera.lookAt(this.camera.curPos);
    this.scene.add(this.camera);
  },
  _computeCamera: function () {
    var curIndex = this.boxes.length - 1;
    var beforeIndex = curIndex - 1;
    var curPos = {
      x: this.boxes[curIndex].position.x,
      z: this.boxes[curIndex].position.z
    }
    
    var beforePos = {
      x: this.boxes[beforeIndex].position.x,
      z: this.boxes[beforeIndex].position.z
    }
    
    var vec = new THREE.Vector3();
    vec.x = (curPos.x + beforePos.x) / 2;
    vec.z = (curPos.z + beforePos.z) / 2;
    vec.y = 0;
    this.camera.targetPos = vec;
  },
  _updateCamera: function () {
    var curPos = this.camera.curPos;
    var targetPos = this.camera.targetPos;
    
    if (curPos.x > targetPos.x || curPos.z > targetPos.z) {
      console.log('x: ', curPos.x, targetPos.x);
      console.log('z: ', curPos.z, targetPos.z);
      curPos.x -= 0.2;
      curPos.z -= 0.2;
      
      if(curPos.x < targetPos.x) {
        curPos.x = targetPos.x;
      }
  
      if(curPos.z < targetPos.z) {
        curPos.z = targetPos.z;
      }
      
      this.camera.lookAt(curPos);
      this._render();
      requestAnimationFrame(this._updateCamera.bind(this));
    }
  },
  _initLight: function () {
    var ambientLight = new THREE.AmbientLight(0x0c0c0c);
    this.scene.add(ambientLight);

    var spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(1000, 1500, 2000);
    spotLight.castShadow = true;
    this.scene.add(spotLight);
  },
  _addBox: function () {
    var box = new THREE.Mesh(new THREE.BoxGeometry(4, 2, 4),
      new THREE.MeshLambertMaterial({
        color: 0xffffff
      })
    );
    
    if(this.boxes.length) {
      var curBox = this.boxes[this.boxes.length-1]
      this.curDirect = this._getDirection();
      this.anotherDirect = this.curDirect == 'z' ? 'x' : 'z';
      box.position[this.curDirect] = curBox.position[this.curDirect] - Math.floor(Math.random()*8 + 7)
      box.position[this.anotherDirect] = curBox.position[this.anotherDirect];
    }
    
    this.scene.add(box);
    this.boxes.push(box);
    
    if(this.boxes.length > 1) {
      this._computeCamera();
    }
  
    if(this.boxes.length > 20) {
      this.scene.remove(this.boxes.shift())
    }
  },
  _addPlayer: function () {
    var geometry = new THREE.CylinderGeometry(.4, .4, 2);
    geometry.translate(0,1,0);
    this.player = new THREE.Mesh(geometry,
      new THREE.MeshLambertMaterial({
        color: 0xff6700
      })
    );
    
    this.player.position.y = 1;
    this._updatePlayerPosition();
    this.scene.add(this.player);
  },
  _getDirection: function () {
    return Math.random() < 0.5 ? 'x': 'z'
  },
  _updatePlayerPosition: function () {
    this.playerPos = {
      x: this.player.position.x,
      y: this.player.position.y,
      z: this.player.position.z
    }
  },
  _handleWindowResize: function () {
    this._setSize();
    var scale = this.scale;
    var width = this.size.width;
    var height = this.size.height;

    this.renderer.setSize(width, height);
    this.camera.left = -width / scale;
    this.camera.right = width / scale;
    this.camera.top = height / scale;
    this.camera.bottom = -height / scale;
    this.camera.updateProjectionMatrix();
    this._render();
  },
  _handleMouseDown: function () {
    this.startTime = +new Date();
    this._changeScaleY();
  },
  _changeScaleY: function() {
    this._render();
    if(!this.jumpReady && this.player.scale.y > 0.1) {
      this.player.scale.y -= 0.01;
      requestAnimationFrame(this._changeScaleY.bind(this))
    }
  },
  _handleMouseup: function () {
    this.jumpReady = true;
    this.moveScale = (+new Date() - this.startTime)/100;
    this.halfOffset = this.moveScale/2;
    this['offset'+this.curDirect] += this.halfOffset + this['oldOffset'+ this.curDirect]; //计算新的对称轴位置
    this['oldOffset'+ this.curDirect] = this.halfOffset; //暂存该方向前一个对称轴距离
    this._renderAnimation();
  },
  _renderAnimation: function () {
    var playerPosition = this.player.position;
    var curDirect = this.curDirect;
    var halfOffset = this.halfOffset;
    var playerScale = this.player.scale;
    var curBox = this.boxes[this.boxes.length-1]
    
    if(Math.abs(playerPosition.y - 1) < 0.01 && playerPosition[curDirect] < this.playerPos[curDirect] - halfOffset) {
      playerPosition.y = 1;
      this._render();
      //位置正确，积分 addBox
      if (Math.abs(playerPosition[curDirect] - curBox.position[curDirect]) < 2) {
        this.score++;
        this._updatePlayerPosition();
        this._updateScore();
        this._addBox();
        this._updateCamera();
        this._render();
      } else if (this.boxes[this.boxes.length-2].position[curDirect] - playerPosition[curDirect] < 2) {
        //还在当前box上
      } else {
        //位置错误，执行失败动画逻辑
        this._failed();
      }
      
      this.jumpReady = false;
      this._updatePlayerPosition();
      return;
    }
  
    playerPosition[curDirect] -= this.halfOffset/10;
    
    // y = -(a+2)/a^2 * (x+offset)^2 + (a+2);
    playerPosition.y = this.playerPos.y - (halfOffset+2)/Math.pow(halfOffset,2) * Math.pow(this['offset'+this.curDirect] + playerPosition[curDirect], 2) + (halfOffset+2);
    
    if(playerScale.y < 1) {
      playerScale.y += 0.05;
      if(playerScale.y > 1) {
        playerScale.y = 1;
      }
    }
    
    this._render();
    requestAnimationFrame(this._renderAnimation.bind(this));
  },
  _updateScore: function () {
    this.topScoreDom.innerText = 'score: ' + this.score;
  },
  _failed: function () {
    this.playing = false;
    var curDirect = this.curDirect;
    var curBox = this.boxes[this.boxes.length-1];
    var oldBoxPosAxis = Math.abs(this.boxes[this.boxes.length-2].position[curDirect]);
    var boxPosAxis = Math.abs(curBox.position[curDirect]);
    var playerPosAxis = Math.abs(this.player.position[curDirect]);
    
    if (playerPosAxis - oldBoxPosAxis > 2 && playerPosAxis - oldBoxPosAxis < 2.4 ||
      playerPosAxis - boxPosAxis > 2 && playerPosAxis - boxPosAxis < 2.4
    ) {
      //掉落在盒子前部边缘
      this.failedType = 'front';
      this._failAnimation();
    } else if (boxPosAxis - playerPosAxis > 2 && boxPosAxis - playerPosAxis < 2.4) {
      //掉落在盒子后部边缘
      this.failedType = 'back';
      this._failAnimation();
    } else {
      //掉落在空白处
      this.failedType = 'black';
      this._failAnimation();
    }
  },
  _failAnimation: function() {
    var anotherDirect = this.anotherDirect;
    var failedType = this.failedType;
    var isRotated = !(Math.abs(this.player.rotation[anotherDirect]) < Math.PI/2)
  
    if (!isRotated) {
      if(failedType == 'front') {
        if(anotherDirect == 'x') {
          this.player.rotation[anotherDirect] -= 0.1;
        } else {
          this.player.rotation[anotherDirect] += 0.1;
        }
        
      } else if(failedType == 'back') {
        if (anotherDirect == 'x') {
          this.player.rotation[anotherDirect] += 0.1;
        } else {
          this.player.rotation[anotherDirect] -= 0.1;
        }
      }
    }
    
    if(isRotated || failedType == 'black') {
      this.player.position.y -= 0.1
      if(this.player.position.y < -1) {
        this._gameOverShow();
        return;
      }
    }
    this._render();
    requestAnimationFrame(this._failAnimation.bind(this));
  },
  _gameOverShow: function () {
    var self = this;
    
    this.scoreDom.innerText = this.score;
    this.buttonDom.addEventListener('click', function () {
      self._restart();
    });
    this.maskDom.style.display = 'flex';
  },
  _restart: function () {
    this.maskDom.style.display = 'none';
    this.score = 0;
    this.playing = true;
    this.scene.remove(this.player);
    for (var i = 0, len = this.boxes.length; i < len; i++) {
      this.scene.remove(this.boxes.pop());
    }
  
    this._resetOffset();
    this._updateScore();
    this._addBox();
    this._addBox();
    this._addPlayer();
    this.camera.curPos = new THREE.Vector3();
    this._updateCamera();
    this._render();
  },
  _render: function () {
    this.renderer.render(this.scene, this.camera);
  }
}

var game = new Game();
game.init();
